/**
 * @Description: 弹框表单组合组件hooks 抛出方法
 * @author SunYanMing<95317356@qq.com>
 * @date 2024/12/6
 */
import { isRef, ref, unref, nextTick, onUnmounted, watch, Ref } from 'vue';
import type {
  FormInstance,
  FormValidateCallback,
  FormItemProp,
  FormValidationResult,
} from 'element-plus';
import { getDynamicProps, error } from './../utlis';
import type { FormSchema, menuType, Partial } from '@/components/BasicModalForm/types';
import { isObject, set, cloneDeep, get, isArray } from 'lodash-es';
import type { BasicPropsActionType } from '@/components/BasicModalForm/src/props';

type ModelMenuType = menuType;

export declare type Arrayable<T> = T | T[];
export interface BaseModelFormActionType {
  submit: () => Promise<void>;
  getFormData: (...paths: string[]) => any;
  updateFormData: (formKey: any | string, value?: any) => void;
  getFormRef: () => FormInstance | undefined;
  open: (type: ModelMenuType, name?: string) => void;
  close: (bol: boolean) => void;
  getLoading: () => boolean;
  getDisabled: () => boolean;
  updateSchema?: (data: Partial<FormSchema<any>> | Partial<FormSchema<any>>[]) => Promise<void>;
  resetSchema?: (data: Partial<FormSchema<any>> | Partial<FormSchema<any>>[]) => Promise<void>;
  setProps: (values: Partial<BasicPropsActionType> | any, path?: string) => void;
  getProps: (...paths: string[]) => BasicPropsActionType;
  getBindValue: () => BasicPropsActionType;
  removeSchemaByField?: (field: string | string[]) => Promise<void>;
  appendSchemaByField?: (
    schema: FormSchema<any> | FormSchema<any>[],
    prefixField: string | undefined,
    first?: boolean | undefined,
  ) => Promise<void>;
  clearValidate: (props?: Arrayable<FormItemProp> | undefined) => Promise<void>;
  validateField: (
    props?: Arrayable<FormItemProp> | undefined,
    callback?: FormValidateCallback | undefined,
  ) => Promise<FormValidationResult>;
  resetFields: (props?: Arrayable<FormItemProp> | undefined) => Promise<void>;
  resetForm?: () => void;
  validate: (callback?: FormValidateCallback | undefined) => Promise<FormValidationResult>;
  scrollToField: (prop: FormItemProp) => Promise<void>;
  [key: string]: any;
}

// props 取代属性传值 v-bind
export function useBaseModelForm(
  props: Ref<BasicPropsActionType> | BasicPropsActionType,
): [register: (_methods: BaseModelFormActionType) => void, methods: BaseModelFormActionType] {
  const modelRef = ref<BaseModelFormActionType | null>(null);
  let propsRef: any = null;

  async function getForm() {
    const form = modelRef.value?.getFormRef();
    if (!form) {
      error(
        'The form instance has not been obtained, please make sure that the form has been rendered when performing the form operation!',
      );
    }
    await nextTick();
    return form as FormInstance;
  }

  async function getModel() {
    const model = unref(modelRef);
    if (!model) {
      error(
        'The model instance has not been obtained, please make sure that the model has been rendered when performing the model operation!',
      );
    }
    await nextTick();
    return model as BaseModelFormActionType;
  }

  function register(_methods: BaseModelFormActionType) {
    onUnmounted(() => {
      modelRef.value = null;
      propsRef = null;
    });
    if (_methods === unref(modelRef)) return;
    modelRef.value = _methods;
    watch(
      () => props,
      () => {
        // 当setProps形参 props 同步更新时，同步更新 propsRef
        if (props) {
          propsRef = isRef(props) ? props.value : props;
          const _props = isRef(props) ? unref(props) : props;
          modelRef.value?.setProps?.(getDynamicProps(_props as BaseModelFormActionType));
        }
      },
      {
        immediate: true,
        deep: true,
      },
    );
  }

  const methods: BaseModelFormActionType = {
    getFormRef(): FormInstance | undefined {
      return unref(modelRef)?.getFormRef();
    },

    open: (type: ModelMenuType, name?: string): void => {
      return unref(modelRef)?.open(type, name);
    },

    close: (bol: boolean): void => {
      return unref(modelRef)?.close(bol);
    },

    getLoading: (): boolean => {
      return unref(modelRef)?.getLoading() as boolean;
    },

    getDisabled: (): boolean => {
      return unref(modelRef)?.getDisabled() as boolean;
    },

    /**
     * 获取表单数据
     * @param paths {string} data.a, data.cc, bbb
     */
    getFormData: (...paths: string[]) => {
      if (isArray(paths) && paths.length) {
        const values = paths.map((path) => get(unref(modelRef)?.getFormData(), path));
        if (values.length === 1) return values[0];
        return values;
      }
      return unref(modelRef)?.getFormData();
    },
    // 整个表单赋值 or 单个变量 obj.a.b = value
    updateFormData: (formDataOrPaths: any | string, value?: any) => {
      if (isObject(formDataOrPaths) && value === undefined) {
        return unref(modelRef)?.updateFormData(formDataOrPaths);
      } else if (typeof formDataOrPaths === 'string') {
        const _formData: object = cloneDeep(unref(modelRef)?.getFormData());
        const formData = set(_formData, formDataOrPaths, value);
        return unref(modelRef)?.updateFormData(formData);
      }
    },

    submit: async (): Promise<any> => {
      const form = await getModel();
      return form.submit();
    },

    getBindValue: () => {
      return unref(modelRef)?.getBindValue() as BasicPropsActionType;
    },

    setProps: (values: Partial<BasicPropsActionType> | any, path?: string) => {
      const model = unref(modelRef);
      if (typeof path === 'string') {
        const props = cloneDeep(model?.getProps()) as object;
        const _props = set(props, path, values);
        set(propsRef, path, values);
        model?.setProps(_props);
      } else {
        Object.keys(values).map((key) => {
          set(propsRef, key, values[key]);
        });
        model?.setProps(values);
      }
    },

    /**
     * 获取组件props数据
     * @param paths {string} data.a, data.cc, bbb
     */
    getProps: (...paths: string[]): BasicPropsActionType | any => {
      if (isArray(paths) && paths.length) {
        const values = paths.map((path) => get(unref(modelRef)?.getProps(), path));
        if (values.length === 1) return values[0];
        return values;
      }
      return unref(modelRef)?.getProps() as BasicPropsActionType;
    },

    // updateSchema: async (data: Partial<FormSchema<any>> | Partial<FormSchema<any>>[]) => {
    //   const model = await getModel();
    //   // await form.updateSchema(data);
    // },
    //
    // resetSchema: async (data: Partial<FormSchema<any>> | Partial<FormSchema<any>>[]) => {
    //   const model = await getModel();
    //   // unref(modelRef)?.updateFormData(data);
    // },
    //
    // removeSchemaByField: async (field: string | string[]) => {
    //   // unref(formRef)?.removeSchemaByField(field);
    // },

    // appendSchemaByField: async (
    // 	schema: FormSchema<any> | FormSchema<any>[],
    // 	prefixField: string | undefined,
    // 	first: boolean
    // ): Promise<any> => {
    // 	const form = await getForm();
    // 	// await form.appendSchemaByField(schema, prefixField, first);
    // },

    scrollToField: async (prop: FormItemProp): Promise<void> => {
      const form = await getForm();
      return form?.scrollToField(prop);
    },

    validate: async (
      callback?: FormValidateCallback | undefined,
    ): Promise<FormValidationResult> => {
      const form = await getForm();
      return form?.validate(callback);
    },

    validateField: async (
      props?: Arrayable<FormItemProp> | undefined,
      callback?: FormValidateCallback | undefined,
    ): Promise<FormValidationResult> => {
      const form = await getForm();
      return form?.validateField(props, callback);
    },

    clearValidate: async (props?: Arrayable<FormItemProp> | undefined) => {
      const form = await getForm();
      await form?.clearValidate(props);
    },

    resetFields: async () => {
      getForm().then(async (form) => {
        await form?.resetFields();
      });
    },
  };

  return [register, methods];
}
